namespace V2TEK.NET.SH.Silverlight.Calendar
{
    using System;
    using System.Globalization;

    public sealed class TimeUtil
    {
        private static DateTime _referenceDate = new DateTime(0x7d0, 1, 1);

        private TimeUtil()
        {
        }

        internal static DateTime AddTimeUnit(DateTime time, long ticks)
        {
            try
            {
                return time.AddTicks(ticks);
            }
            catch (Exception)
            {
                return ((ticks < 0L) ? DateTime.MinValue : DateTime.MaxValue);
            }
        }

        public static DateTime AddTimeUnit(DateTime time, TimeUnit unit, int count)
        {
            return AddTimeUnit(CultureInfo.CurrentCulture.DateTimeFormat.Calendar, time, unit, count);
        }

        internal static DateTime AddTimeUnit(DateTime time, TimeSpan unitDuration, int count)
        {
            try
            {
                return time.AddTicks(unitDuration.Ticks * count);
            }
            catch (Exception)
            {
                return ((count < 0) ? DateTime.MinValue : DateTime.MaxValue);
            }
        }

        internal static DateTime AddTimeUnit(Calendar calendar, DateTime time, TimeUnit unit, int count)
        {
            try
            {
                switch (unit)
                {
                    case TimeUnit.Century:
                        return calendar.AddYears(time, 100 * count);

                    case TimeUnit.Decade:
                        return calendar.AddYears(time, 10 * count);

                    case TimeUnit.Year:
                        return calendar.AddYears(time, count);

                    case TimeUnit.HalfYear:
                        return calendar.AddMonths(time, 6 * count);

                    case TimeUnit.Quarter:
                        return calendar.AddMonths(time, 3 * count);

                    case TimeUnit.Month:
                        return calendar.AddMonths(time, count);

                    case TimeUnit.Week:
                        return calendar.AddWeeks(time, count);

                    case TimeUnit.Day:
                        return calendar.AddDays(time, count);

                    case TimeUnit.HalfDay:
                        return calendar.AddHours(time, 12 * count);

                    case TimeUnit.Hour:
                        return calendar.AddHours(time, count);

                    case TimeUnit.Minute:
                        return calendar.AddMinutes(time, count);

                    case TimeUnit.Second:
                        return calendar.AddSeconds(time, count);

                    case TimeUnit.Millisecond:
                        return calendar.AddMilliseconds(time, (double) count);
                }
                return DateTime.MaxValue;
            }
            catch (Exception)
            {
                return ((count < 0) ? DateTime.MinValue : DateTime.MaxValue);
            }
        }

        public static DateTime GetNextTime(DateTime time, TimeUnit unit, int steps)
        {
            time = AddTimeUnit(TrimTimeUnit(CultureInfo.CurrentUICulture.DateTimeFormat, _referenceDate, time, unit, steps), unit, steps);
            return time;
        }

        public static DateTime GetPreviousTime(DateTime time, TimeUnit unit, int steps)
        {
            time = TrimTimeUnit(CultureInfo.CurrentUICulture.DateTimeFormat, _referenceDate, time, unit, steps);
            return time;
        }

        internal static long GetTicksPerUnit(TimeUnit unit)
        {
            switch (unit)
            {
                case TimeUnit.Century:
                    return 0x7009d32da30000L;

                case TimeUnit.Decade:
                    return 0xb342eb7c38000L;

                case TimeUnit.Year:
                    return 0x11ed178c6c000L;

                case TimeUnit.HalfYear:
                    return 0x8f68bc636000L;

                case TimeUnit.Quarter:
                    return 0x47b45e31b000L;

                case TimeUnit.Month:
                    return 0x185c22ce4000L;

                case TimeUnit.Week:
                    return 0x58028e44000L;

                case TimeUnit.Day:
                    return 0xc92a69c000L;

                case TimeUnit.HalfDay:
                    return 0x649534e000L;

                case TimeUnit.Hour:
                    return 0x861c46800L;

                case TimeUnit.Minute:
                    return 0x23c34600L;

                case TimeUnit.Second:
                    return 0x989680L;

                case TimeUnit.Millisecond:
                    return 0x2710L;
            }
            throw new NotImplementedException("Cannot compute the number of ticks for this unit !" + unit.ToString());
        }

        internal static TimeUnit GetTimeUnit(long ticks)
        {
            if (ticks >= 0x7009d32da30000L)
            {
                return TimeUnit.Century;
            }
            if (ticks >= 0xb342eb7c38000L)
            {
                return TimeUnit.Decade;
            }
            if (ticks >= 0x11ed178c6c000L)
            {
                return TimeUnit.Year;
            }
            if (ticks >= 0x8f68bc636000L)
            {
                return TimeUnit.HalfYear;
            }
            if (ticks >= 0x47b45e31b000L)
            {
                return TimeUnit.Quarter;
            }
            if (ticks >= 0x185c22ce4000L)
            {
                return TimeUnit.Month;
            }
            if (ticks >= 0x58028e44000L)
            {
                return TimeUnit.Week;
            }
            if (ticks >= 0xc92a69c000L)
            {
                return TimeUnit.Day;
            }
            if (ticks >= 0x649534e000L)
            {
                return TimeUnit.HalfDay;
            }
            if (ticks >= 0x861c46800L)
            {
                return TimeUnit.Hour;
            }
            if (ticks >= 0x23c34600L)
            {
                return TimeUnit.Minute;
            }
            if (ticks >= 0x989680L)
            {
                return TimeUnit.Second;
            }
            return TimeUnit.Millisecond;
        }

        internal static TimeUnit GetTimeUnit(TimeSpan duration)
        {
            return GetTimeUnit(duration.Ticks);
        }

        internal static double GetTimeUnits(DateTime reference, DateTime time, TimeUnit unit)
        {
            long num = time.Ticks - reference.Ticks;
            long ticksPerUnit = GetTicksPerUnit(unit);
            return (((double) num) / ((double) ticksPerUnit));
        }

        public static DateTime Max(DateTime t1, DateTime t2)
        {
            if (t1 < t2)
            {
                return t2;
            }
            return t1;
        }

        public static TimeSpan Max(TimeSpan t1, TimeSpan t2)
        {
            if (t1.Ticks < t2.Ticks)
            {
                return t2;
            }
            return t1;
        }

        public static DateTime Min(DateTime t1, DateTime t2)
        {
            if (t1 > t2)
            {
                return t2;
            }
            return t1;
        }

        public static TimeSpan Min(TimeSpan t1, TimeSpan t2)
        {
            if (t1.Ticks > t2.Ticks)
            {
                return t2;
            }
            return t1;
        }

        private static DateTime TrimMonths(Calendar calendar, DateTime reference, DateTime time, int steps, int unitsPerYear)
        {
            int year = calendar.GetYear(time);
            int num2 = calendar.GetMonthsInYear(year) / unitsPerYear;
            int num3 = calendar.GetMonth(reference) - 1;
            int num4 = num3 / num2;
            reference = calendar.AddMonths(reference.Date, (num4 * num2) - num3);
            int num5 = (year * unitsPerYear) + ((calendar.GetMonth(time) - 1) / num2);
            int num6 = (calendar.GetYear(reference) * unitsPerYear) + ((calendar.GetMonth(reference) - 1) / num2);
            int num7 = num5 - num6;
            int num8 = (num7 / steps) * steps;
            if ((num7 < 0) && ((num7 % steps) != 0))
            {
                num8 -= steps;
            }
            return calendar.AddMonths(reference, num8 * num2);
        }

        internal static DateTime TrimTimeUnit(DateTime reference, DateTime time, long duration)
        {
            if (duration <= 0L)
            {
                throw new ArgumentOutOfRangeException("duration", "TrimTimeUnit : duration must be greater than zero !");
            }
            long num = time.Ticks - reference.Ticks;
            long num2 = (num / duration) * duration;
            if ((num < 0L) && ((num % duration) != 0L))
            {
                num2 -= duration;
            }
            double num3 = reference.Ticks + num2;
            if ((num2 > 0L) && (num3 >= DateTime.MaxValue.Ticks))
            {
                return DateTime.MaxValue;
            }
            if ((num2 < 0L) && (num3 < 0.0))
            {
                return DateTime.MinValue;
            }
            return new DateTime(reference.Ticks + num2);
        }

        internal static DateTime TrimTimeUnit(DateTime reference, DateTime time, TimeSpan unitDuration, int steps)
        {
            return TrimTimeUnit(reference, time, unitDuration.Ticks * steps);
        }

        internal static DateTime TrimTimeUnit(DateTimeFormatInfo dateFormat, DateTime reference, DateTime time, TimeUnit unit, int steps)
        {
            Calendar calendar = dateFormat.Calendar;
            try
            {
                switch (unit)
                {
                    case TimeUnit.Century:
                    {
                        int num = 100 * steps;
                        int year = (calendar.GetYear(time) / num) * num;
                        return calendar.ToDateTime(year, 1, 1, 0, 0, 0, 0);
                    }
                    case TimeUnit.Decade:
                    {
                        int num3 = 10 * steps;
                        int num4 = (calendar.GetYear(time) / num3) * num3;
                        return calendar.ToDateTime(num4, 1, 1, 0, 0, 0, 0);
                    }
                    case TimeUnit.Year:
                    {
                        int num5 = (calendar.GetYear(time) / steps) * steps;
                        return calendar.ToDateTime(num5, 1, 1, 0, 0, 0, 0);
                    }
                    case TimeUnit.HalfYear:
                        return TrimMonths(calendar, reference, time, steps, 2);

                    case TimeUnit.Quarter:
                        return TrimMonths(calendar, reference, time, steps, 4);

                    case TimeUnit.Month:
                        return TrimMonths(calendar, reference, time, steps, calendar.GetMonthsInYear(calendar.GetYear(time)));

                    case TimeUnit.Week:
                    {
                        DayOfWeek firstDayOfWeek = dateFormat.FirstDayOfWeek;
                        DayOfWeek dayOfWeek = calendar.GetDayOfWeek(reference);
                        int days = (int) (firstDayOfWeek - dayOfWeek);
                        reference = calendar.AddDays(reference.Date, days);
                        return TrimTimeUnit(reference, time, GetTicksPerUnit(TimeUnit.Week) * steps);
                    }
                }
                return TrimTimeUnit(reference.Date, time, GetTicksPerUnit(unit) * steps);
            }
            catch (Exception)
            {
                return DateTime.MinValue;
            }
        }
    }
}

